﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using GUI.Custom_Classes;
using Tedds_Tool;
using System.ComponentModel;
using Ins = Tedds_Tool.Instructions;
using System.IO;
using System.Windows.Media;
using System.Windows.Controls;
using System.Media;
using LEET.LEETCommon.Model;
using System.Windows.Threading;
using LEET.Application;
using LEET.CommonInterfaces;
using Compiled_Tests;
using System.Diagnostics;

namespace GUI.Tabs
{
    class ScriptController
    {
        private ScriptViewer Script_Viewer;
        public ListBox ScriptViewerLineNums;
        private String currentScriptName = null;
        Facade f { get; set; }
        IScriptFinder ScriptFinder { get; set; }
        private delegate bool BoolDelegate();
        private IAlerts alerts;

        public ScriptController(ScriptViewer script, Facade f, IScriptFinder finder)
        {
            this.Script_Viewer = script;
            this.f = f;
            this.ScriptFinder = finder;
            Script_Viewer.scriptchanged = false;
            this.alerts = new MessageBoxAlerts(Window.GetWindow(Script_Viewer as ScriptViewer));
            
        }

        public void CompileTest(object sender, RoutedEventArgs args)
        {
            Compile(f.CompileAsTest);
        }

        public void CompileDemo(object sender, RoutedEventArgs e)
        {
            Compile(f.CompileAsDemo);
        }

        delegate void CompileCall(String scriptName);
        void Compile(CompileCall call)
        {
            save_or_load();

            if (!string.IsNullOrEmpty(currentScriptName))
            {
                Dispatcher.CurrentDispatcher.Thread.IsBackground = true;
                Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
                {
                    try
                    {
                        call(currentScriptName);
                        alerts.Alert("Script compilation completed sucessfully.");
                    }
                    catch (InvalidScriptException e)
                    {
                        //empty script
                        alerts.Error(e.Message);
                    }
                    catch (Ins.InvalidInstructionException e)
                    {
                        //bad syntax
                        alerts.Error("Illegal syntax: " + e.Message);
                    }
                }, DispatcherPriority.Background);
            }
        }

        public void RunTest(object sender, RoutedEventArgs e)
        {
            Run(f.RunScript);
        }

        public void RunDemo(object sender, RoutedEventArgs e)
        {
            Run(f.RunDemo);
        }

        private delegate LEETTestResult RunCall(String scriptName);
        private void Run(RunCall call)
        {
            save_or_load();
            if (!string.IsNullOrEmpty(currentScriptName))
            {
                RefreshErrorLine(null, null);
                //TODO: explain WTF this is called
                CompileTest(null, null);
                Window w = Window.GetWindow(Script_Viewer as ScriptViewer);

                w.WindowState = WindowState.Minimized;
                Dispatcher.CurrentDispatcher.Thread.IsBackground = true;
                Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
                {
                    try
                    {
                        StringBuilder script = new StringBuilder();
                        foreach (String o in Script_Viewer)
                        {
                            script.Append(o).Append("\r\n");
                        }
                        LEETTestResult testResult = call(currentScriptName);

                        if (testResult.Failed)
                        {
                            try
                            {
                                Script_Viewer.PaintError(testResult.FailingLine(currentScriptName));
                            }
                            catch (ArgumentException) { }
                        }
                        Script_Viewer.TestFinished();

                        w.WindowState = WindowState.Normal;
                        w.Focus();
                        if (!string.IsNullOrEmpty(testResult.outputmsg))
                            alerts.Alert(testResult.outputmsg);
                        else if (!string.IsNullOrEmpty(testResult.errormsg))
                            alerts.Error(testResult.errormsg);
                    }
                    catch
                    {
                        int x = 5;
                    }
                }, DispatcherPriority.Background);
            }
        }

        /// <summary>
        /// Refreshes the script and removes any red error lines if one was found in a previous run.
        /// </summary>
        public void RefreshErrorLine(object sender, RoutedEventArgs e)
        {
            Script_Viewer.UnPaintErrors();
        }

        public void Save(object sender, RoutedEventArgs args)
        {
            if (Script_Viewer.scriptchanged == false)
                return;

            RefreshErrorLine(null, null);
            ScriptNamerDialog dialog;
            Window owner = Window.GetWindow(Script_Viewer as FrameworkElement);

            if (String.IsNullOrEmpty(currentScriptName))
                dialog = new ScriptNamerDialog(Window.GetWindow(Script_Viewer as ScriptViewer));
            else
                dialog = new ScriptNamerDialog(Window.GetWindow(Script_Viewer as ScriptViewer), currentScriptName);

            dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            dialog.Owner = owner;
            dialog.ShowDialog();
            currentScriptName = dialog.Return;


            if (dialog.DialogResult == true && !string.IsNullOrEmpty(currentScriptName))
            {
                if (String.IsNullOrEmpty(currentScriptName))
                {
                    alerts.Alert("You must specify a name for the script.");
                }
                else
                {
                    bool saveAnyway = true;

                    if (ScriptFinder.ListTestScripts().Contains(currentScriptName))
                        saveAnyway = (bool)owner.Dispatcher.Invoke((BoolDelegate)delegate()
                        {
                            return alerts.Confirm("File already exists. Do you wish to overwrite?");
                        });

                    Script_Viewer.Dispatcher.Thread.IsBackground = true;
                    DispatcherOperation op = Script_Viewer.Dispatcher.BeginInvoke((Action)delegate
                    {
                        if (saveAnyway)
                        {
                            f.SaveScript(currentScriptName, Script_Viewer);
                            if (sender != null)
                                alerts.Alert("Script saved saved successfully.");
                            Script_Viewer.scriptchanged = false;
                        }
                            
                    }, DispatcherPriority.Send);
                }
                Script_Viewer.scriptchanged = false;
            }
            else
                Script_Viewer.scriptchanged = true;
            
        }

        public void Load(object sender, RoutedEventArgs args)
        {
            RefreshErrorLine(null, null);
            ScriptSelectorDialog selectorDialog = new ScriptSelectorDialog(Window.GetWindow(Script_Viewer as ScriptViewer), ScriptFinder.ListTestScripts());
            selectorDialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            selectorDialog.ShowDialog();
            selectorDialog.Focus();

            String scriptName = selectorDialog.Return;
            currentScriptName = scriptName;

            if (scriptName != null && selectorDialog.DialogResult == true)
            {
                List<string> embeds = new List<string>();
                Script_Viewer.Dispatcher.Thread.IsBackground = true;
                Script_Viewer.Dispatcher.BeginInvoke((Action)delegate
                {
                    IEnumerable<String> script = f.LoadScript(currentScriptName);
                    foreach (String line in script)
                    {
                        if (line.StartsWith("EMBED") && !line.Contains(currentScriptName))
                            embeds.Add(line.Substring(0));
                        Script_Viewer.AddScriptItem(new Event_Pair(line));
                    }
                    Script_Viewer.scriptchanged = false;
                }, DispatcherPriority.Send);
            }
            Script_Viewer.scriptchanged = false;
        }

        public void Clear(object sender, RoutedEventArgs args)
        {
            Script_Viewer.Clear();
            currentScriptName = null;
            Script_Viewer.scriptchanged = false;
        }

        private void save_or_load()
        {
            if (Script_Viewer.IsEmpty)
                Load(null, null);
            else if (Script_Viewer.scriptchanged)
                Save(null, null);
        }

        /// <summary>
        /// Returns the embedded scroll object associated with the specified root element.
        /// </summary>
        private DependencyObject GetScrollChild(DependencyObject n)
        {
            bool terminate = false;
            object child = null;
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
            {
                child = VisualTreeHelper.GetChild(n, i);
                if (child is ScrollViewer)
                    return child as DependencyObject;
            }
            if (!terminate)
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(n); i++)
                    child = GetScrollChild(VisualTreeHelper.GetChild(n, i));
            return child as DependencyObject;
        }
    }
}
